package com.demo.ftp;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.log4j.Logger;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class FtpUtils {

    private static Logger log = Logger.getLogger(FtpUtils.class);

    /**
     * 获取FTPClient对象
     */
    public static FTPClient getFTPClient(FtpProperty property) {
        FTPClient ftpClient=null;
        try {
            ftpClient = new FTPClient();
            // 设置默认超时时间
            ftpClient.setDefaultTimeout(120  * 1000);
            // 设置连接超时时间
            ftpClient.setConnectTimeout(120 * 1000);
            // 设置数据超时时间
            ftpClient.setDataTimeout(120 * 1000);
            //设置编码
            ftpClient.setControlEncoding(property.getFtpEncoding());
            FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
            conf.setServerLanguageCode("zh");
            ftpClient.configure(conf);
            ftpClient.enterLocalPassiveMode();
            ftpClient.connect(property.getFtpHost(), property.getFtpPort());// 连接FTP服务器
            ftpClient.login(property.getFtpUserName(), property.getFtpPassword());// 登陆FTP服务器
            if (!FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                log.error("FTP连接失败！"+ftpClient.getReplyString());
                ftpClient.disconnect();
            }else {
                //设置文件类型
                ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
                //切换工作目录
                ftpClient.changeWorkingDirectory(property.getFtpPath());
                ftpClient.setBufferSize(1024);
                log.info("FTP连接成功!");
            }
        } catch (Exception e) {
            log.error("FTP连接错误！",e);
        }
        return ftpClient;
    }

    /*
     * 从FTP服务器下载文件
     *
     * @param ftpHost FTP IP地址
     *
     * @param ftpUserName FTP 用户名
     *
     * @param ftpPassword FTP用户名密码
     *
     * @param ftpPort FTP端口
     *
     * @param ftpPath FTP服务器中文件所在路径 格式： ftptest/aa
     *
     * @param localPath 下载到本地的位置 格式：H:/download
     *
     * @param fileName 文件名称
     */
    public static final long monitorInternal = 5 * 1000L;

    /**
     * FTP文件是否准备好
     * @param property
     * @param configs
     * @return
     */
    public static boolean isFtpFileReady(FtpProperty property, List<String> configs) {
        FTPClient ftpClient = null;

        try {
            //获取 ftp客户端
            ftpClient = getFTPClient(property);
            for (int i = 0; i < configs.size(); i++) {
                //判断文件是否都存在
                InputStream inputStream = ftpClient.retrieveFileStream(configs.get(i));

                if (inputStream == null || ftpClient.getReplyCode() == FTPReply.FILE_UNAVAILABLE) {
                    log.info(configs.get(i) + " is not available for now.");
                    return false;
                } else {
                    log.info(configs.get(i) + " is available.");
                    inputStream.close();
                    ftpClient.completePendingCommand();
                }
            }
            ftpClient.logout();
            return true;
        } catch (Exception e) {
            log.error(e);
            return false;
        }
    }

    /**
     * 下载FTP文件
     * @param property
     * @param configs
     * @param monitorDuration
     * @return
     */
    public static boolean downloadFtpFile(FtpProperty property, List<String> configs, List<String> paths, long monitorDuration) {
        //设置一个默认的超时时间
        if (monitorDuration <= 0) {
            monitorDuration = 1000 * 30L;
        }
        try {
            //ftp 的一些设置
            FTPClient ftpClient = getFTPClient(property);
            //获取当前系统的时间,计算是否超时
            long startTime = System.currentTimeMillis();
            FTPFile[] listFiles = ftpClient.listFiles();
            //获取当前目录的文件列表
            if (listFiles == null || listFiles.length <= 0) {
                log.warn("该文件夹下没有文件");
                return false;
            }
            log.info("开始下载FTP文件...");
            for (int i = 0; i < configs.size(); i++) {
                //获取下载的文件名
                String fileName = configs.get(i);
                //遍历所有的文件
                FTPFile ftpFile = null;
                for (FTPFile file : listFiles) {
                    if (file.getName().equals(fileName)) {
                        ftpFile = file;
                        break;
                    }
                }
                //文件未找到，日志记录并跳转下一条
                if (ftpFile == null) {
                    log.warn(fileName + " is not found!");
                    continue;
                }
                //判断是否超时
                if (System.currentTimeMillis() - startTime > monitorDuration) {
                    for (; i < configs.size(); i++) {
                        log.warn("TimeOut!File " + configs.get(i) + " is not download!");
                    }
                    return false;
                }
                log.info("正在下载:"+ftpFile);
                //获取本地文件路径
                String filePath;
                if (paths.size() == 1) {
                    filePath = paths.get(0);
                } else {
                    filePath = paths.get(i);
                }
                File backupFile = new File(FilePathUtils.filterFilePath(filePath + fileName));
                //当前目录不存在就重新创建文件夹
                if (!backupFile.getParentFile().exists()) {
                    backupFile.getParentFile().mkdirs();
                }
                //判断本地文件是否存在
                if (backupFile.exists()) {
                    if (backupFile.delete()) {
                        log.info("File " + fileName + " is already downloaded, delete it now. ");
                    }
                }
                try (InputStream is = ftpClient.retrieveFileStream(fileName);
                     OutputStream os = new FileOutputStream(backupFile)) {
                    //当前目录是否存在这个文件
                    if (is == null || ftpClient.getReplyCode() == FTPReply.FILE_UNAVAILABLE) {
                        log.warn(String.format("FTP文件下载失败！FileName:%s，Error:%s",
                                fileName,ftpClient.getReplyString()));
                        //如果文件不能下载那么添加到configs,尝试多次下载
                        configs.add(fileName);
                        //删除当前的文件,加入到后面重新下载
                        configs.remove(i);
                        Thread.sleep(monitorInternal);
                        i--;
                        continue;
                    } else {
                        is.close();
                        //开始下载文件
                        ftpClient.completePendingCommand();
                        //开始下载文件
                        ftpClient.retrieveFile(fileName, os);
                        os.flush();
                        log.info("File " + fileName + " downloaded. ");
                    }
                } catch (Exception e) {
                    log.error(String.format("FTP文件下载异常！FileName:%s", fileName), e);
                }
            }
            //退出登录
            ftpClient.logout();
            ftpClient.disconnect();
            return true;
        } catch (Exception e) {
            log.error("FTP文件下载出现异常！", e);
            return false;
        }
    }

    /**
     * 当前目录下的所有文件列表
     * @param property
     * @return
     */
    public static List<String> ListFile(FtpProperty property){
        //获取ftp客户端
        FTPClient ftpClient = getFTPClient(property);
        List<String> fileNames = new ArrayList<String>();
        try {
            //获取当前目录所有的文件
            FTPFile[] listFiles = ftpClient.listFiles();
            //遍历所有的文件获取所有的文件名
            for(FTPFile f:listFiles ){
                fileNames.add(f.getName());
            }
        } catch (IOException e) {
            log.error("FTP ListFile Error!",e);
        }
        //返回当前目录下所有文件的文件名
        return fileNames;
    }

    /**
     * 上传到FTP指定目录下
     *
     * @param property
     *            FTP信息
     * @param fileName
     *            文件名称
     * @param dirPath
     *            存储的文件夹绝对路径
     * @param file
     *            文件
     * @return
     */
    public static boolean upload(FtpProperty property, String fileName, String dirPath, File file) {
        boolean flag = false;
        try {
            // 获取ftp客户端
            log.info("CommonUtils start to upload ftp file: " + fileName + " to " + dirPath);

            FTPClient ftpClient = getFTPClient(property);
            ftpClient.makeDirectory(dirPath);
            ftpClient.changeWorkingDirectory(dirPath);
            ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
            FileInputStream input = new FileInputStream(file);
            log.info("CommonUtils start to store ftp file");
            flag = ftpClient.storeFile(fileName, input);
            log.info("CommonUtils store ftp file success");

            input.close(); // 关闭输入流
        } catch (IOException e) {
            log.error("FTP上传异常", e);
        }
        return flag;
    }

}
